<?php

namespace Hyperfx\ServiceGovernanceNacos;

use Grpc\Base\Common\V1;
use Hyperf\Contract\ConfigInterface;
use Hyperf\GrpcClient\Exception\GrpcClientException;
use Hyperf\ServiceGovernance\DriverInterface;
use Hyperfx\Framework\Constants\ErrorCode;
use Hyperfx\Framework\Logger\Logx;
use Hyperfx\ServiceGovernanceNacos\NodeSelector\NodeSelector;
use Psr\Container\ContainerInterface;

class ServiceClient
{
    private array $configProviders = [];
    /**
     * @var DriverInterface
     */
    protected $driver;

    /**
     * @var $nodeSelector NodeSelector
     */
    private $nodeSelector;

    private int $lastClearPoolTime = 0;

    private array $servicePools = [];

    public function __construct(ContainerInterface $container)
    {
        $this->configProviders = $container->get(ConfigInterface::class)->get('services.providers', []);
        $this->driver = $container->get(\Hyperfx\ServiceGovernanceNacos\NacosDriver::class);
        $this->nodeSelector = $container->get(NodeSelector::class);
    }

    private function selectService($service): array {
        if (!isset($this->configProviders[$service])) {
            throw new GrpcClientException(sprintf('service [%s] not found', (string) $service), ErrorCode::SERVER_ERROR);
        }
        return $this->configProviders[$service];
    }

    private function getServicePoolKey($service, $hostname): string
    {
        return sprintf('%s@%s', $service, $hostname);
    }

    private function clearTimeoutPool(): void
    {
        $time = time();
        // 20秒内只请理一次
        if ($this->lastClearPoolTime + 20 > $time ) {
            return;
        }
        $this->lastClearPoolTime = $time;
        foreach ($this->servicePools as $key => $pool) {
            // 5分种不用的清理掉
            if ($pool['time'] < $time - 60 * 5) {
                unset($this->servicePools[$key]);
            }
        }
    }
    public function create($service) {
        $vars = $this->select($service);
        $key = $this->getServicePoolKey($service, $vars[0]);
        if (!isset($this->servicePools[$key])) {
            $this->servicePools[$key] = [
                'client' => make($service, $vars),
                'time' => time(),
            ];
        } else {
            $this->servicePools[$key]['time'] = time();
        }
        $this->clearTimeoutPool();
        return $this->servicePools[$key]['client'];
    }

    public function select($service): array {
        $currentService = $this->selectService($service);
        $node = $this->nodeSelector->select($currentService['service']);
        $hostname = sprintf('%s:%s', $node->host, $node->port);
        Logx::get()->debug('select service', [
            'service' => $service,
            'hostname' => $hostname,
        ]);
        return [$hostname, $currentService['options']];
    }
}
